package com.xxlie.auth.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxlie.auth.security.CustomCorsFilter;
import com.xxlie.auth.security.RestAuthenticationEntryPoint;
import com.xxlie.auth.security.auth.ajax.AjaxAuthenticationProvider;
import com.xxlie.auth.security.auth.ajax.AjaxLoginAdminProcessingFilter;
import com.xxlie.auth.security.auth.ajax.AjaxLoginPhoneProcessingFilter;
import com.xxlie.auth.security.auth.ajax.AjaxLoginProcessingFilter;
import com.xxlie.auth.security.auth.ajax.AjaxPhoneAuthenticationProvider;
import com.xxlie.auth.security.auth.jwt.JwtAuthenticationProvider;
import com.xxlie.auth.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
import com.xxlie.auth.security.auth.jwt.SkipPathRequestMatcher;
import com.xxlie.auth.security.auth.jwt.extractor.TokenExtractor;
import com.xxlie.auth.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Arrays;
import java.util.List;

/**
 * 安全登录配置
 * WebSecurityConfig
 * @author xxlie.com
 * @since 2017/7/30
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthPathSettings authPathSettings;
    @Autowired
    private RestAuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private AuthenticationSuccessHandler successHandler;
    @Autowired
    private AuthenticationFailureHandler failureHandler;
    @Autowired
    private AjaxAuthenticationProvider ajaxAuthenticationProvider;
    @Autowired
    private JwtAuthenticationProvider jwtAuthenticationProvider;

    @Autowired
    private TokenExtractor tokenExtractor;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private UserService userService;

    @Autowired
    private AjaxPhoneAuthenticationProvider ajaxPhoneAuthenticationProvider;

    protected AjaxLoginProcessingFilter buildAjaxLoginProcessingFilter() {
        AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(authPathSettings.getLoginUrls(), successHandler, failureHandler, objectMapper);
        filter.setAuthenticationManager(this.authenticationManager);
        return filter;
    }

    protected AjaxLoginPhoneProcessingFilter buildAjaxLoginPhoneProcessingFilter() {
        AjaxLoginPhoneProcessingFilter filter = new AjaxLoginPhoneProcessingFilter(authPathSettings.getMobileLoginUrl(), successHandler, failureHandler, objectMapper);
        filter.setAuthenticationManager(this.authenticationManager);
        return filter;
    }

    protected AjaxLoginAdminProcessingFilter buildAjaxLoginAdminProcessingFilter() {
        AjaxLoginAdminProcessingFilter filter = new AjaxLoginAdminProcessingFilter(authPathSettings.getAdminLoginUrl(), successHandler, failureHandler, objectMapper);
        filter.setAuthenticationManager(this.authenticationManager);
        return filter;
    }

    protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() {
        List<String> pathsToSkip = Arrays.asList(authPathSettings.getSkipUrls());
        List<String> processingPath = Arrays.asList(authPathSettings.getAuthUrls());
        SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, processingPath);
        JwtTokenAuthenticationProcessingFilter filter
                = new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher, userService);
        filter.setAuthenticationManager(this.authenticationManager);
        return filter;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(ajaxAuthenticationProvider);
        auth.authenticationProvider(jwtAuthenticationProvider);
        auth.authenticationProvider(ajaxPhoneAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable() // We don't need CSRF for JWT based authentication
                .exceptionHandling()
                .authenticationEntryPoint(this.authenticationEntryPoint)

                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .authorizeRequests()
                .antMatchers(authPathSettings.getSkipUrls()).permitAll() // sms send end-point
                .and()
                .authorizeRequests()
                .antMatchers(authPathSettings.getAuthUrls()).authenticated() // Protected API End-points
                .and()
                .addFilterBefore(new CustomCorsFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(buildAjaxLoginAdminProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(buildAjaxLoginPhoneProcessingFilter(), UsernamePasswordAuthenticationFilter.class);

        http.headers().frameOptions().disable();
    }

}
